`timescale 1ns/100ps
`default_nettype none

/* NOTE:
* - 输入图像数据流程控制
* - 将行缓冲中的数据推入图像处理流水线
*/

/* NOTE:
* 行控
* - 像素数据是顺序的
* - 按照对开分区，依次写入
*
* 列控
* - 按照一行数据组port，依次写入
*
*
*/

module rgb_pixel_save_ctrl (
    // system signal
    input  wire         I_sclk,  // 125M
    input  wire         I_rst_n,
    // frame sync
    input  wire         I_frame_start,
    // // config
    input  wire [3:0]   I_cfg_sector_count, // 分区数量
    input  wire [10:0]  I_cfg_sector_width, // 每个区域宽度（总是4的倍数)
    input  wire [10:0]  I_cfg_sector_height,
    input  wire [10:0]  I_cfg_win_col_num,
    input  wire [1:0]   I_cfg_box_dir,       // 箱体方向
    // addr decoder
    output wire         O_decode_req,    // 行地址译码请求
    output wire [9:0]   O_decode_row,    // 行号
    output wire [3:0]   O_decode_sector, // 分区号
    input  wire         I_decode_done,   // 译码完成
    input  wire [20:0]  I_decode_addr,   // 译码结果
    
    output wire [9:0]   O_decode_col_addr,  //列映射 地址请求
    input  wire [9:0]   I_decode_col_addr,  //列映射 映射结果
    
    // line buffer
    input  wire         I_row_start,   // 单周期脉冲，表明一行数据正在缓冲中，此时可以预读逐点调整系数
    input  wire [9:0]   I_row_num,     // 当前行号
    input  wire         I_row_ready,   // 电平有效，表明本行已缓冲完毕，可以读取数据
    output wire         O_row_ack,     // 当前行数据使用完毕
    output wire         O_pixel_req,   // 请求读取本行数据（顺序读取）
    input  wire [23:0]  I_pixel_data,  // 像素数据
    
    output wire [9:0]   O_pixel_addr,
    
    // write pixel
    output wire         O_write_valid,
    output wire         O_write_start, // 开始写入
    input  wire         I_write_busy,  // 正在写入
    output wire [20:0]  O_write_addr,  // 写入SDRAM地址
    output wire [5:0]   O_write_len,   // 写入长度，最大32bit x 128/4
    output wire [23:0]  O_write_data   // 写入数据
);
//------------------------Parameter----------------------

assign O_pixel_addr = I_decode_col_addr ;
// fsm
localparam [3:0]
    IDLE  = 0,
    COE0  = 1,
    COE1  = 2,
    READY = 3,
    DEC0  = 4,
    DEC1  = 5,
    WPREP = 6,
    WREQ  = 7,
    WDATA = 8,
    WBUSY = 9,
    LOOP0 = 10,
    LOOP1 = 11,
    ACK   = 12,
    WAIT0 = 13,
    WAIT1 = 14;

// box direction
localparam [1:0]
    LANDSCAPE = 0, // 横向
    PORTRAIT0 = 1, // 纵向，第一个端口在左侧
    PORTRAIT1 = 2; // 纵向，第一个端口在右侧

//------------------------Local signal-------------------
// fsm
reg  [3:0]  state;
reg  [3:0]  next;
reg         write_over;
reg  [10:0] total_count; // 一个区域内的总像素数
reg  [8:0]  pixel_count; // 一次写的总像素数，小于256
reg  [8:0]  pixel_max;   // 一次写的最大像素数
reg         pixel_req;
reg  [3:0]  sector_id;
reg  [9:0]  row_num;

// write pixel
reg  [20:0] write_addr;
reg  [8:0]  write_len;

reg  [9:0]  pixel_addr;

//------------------------Instantiation------------------

//------------------------Body---------------------------
//{{{+++++++++++++++++++++fsm++++++++++++++++++++++++++++
// state
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        state <= IDLE;
    else if (I_frame_start)
        state <= IDLE;
    else
        state <= next;
end
// next
always @(*) begin
    case (state)
        IDLE: begin
            if (I_row_start)
                next = READY;
            else
                next = IDLE;
        end

        READY: begin
            if (I_row_ready)
                next = DEC0;
            else
                next = READY;
        end

        DEC0: begin
            next = DEC1;
        end

        DEC1: begin
            if (I_decode_done)
                next = WAIT0;
            else
                next = DEC1;
        end

        WAIT0:begin
            if (~I_write_busy)
                next = WAIT1;
            else 
                next = WAIT0;
        end
        
        WAIT1:begin
                next = WPREP;
        end
        
        WPREP: begin
            next = WREQ;
        end

        WREQ: begin
            next = WDATA;
        end

        WDATA: begin
            if (write_over)
                // next = LOOP0;
                next = WBUSY;
            else
                next = WDATA;
        end

        WBUSY: begin
            if (~I_write_busy)
                next = LOOP0;
            else
                next = WBUSY;
        end
        
        
        LOOP0: begin
            if (total_count > 1'b0)
                // next = WPREP;
                next = WAIT0;
            else
                next = ACK;
        end

        ACK: begin
            next = IDLE;
        end

        default: begin
            next = IDLE;
        end
    endcase
end

// write_over
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        pixel_addr <= 1'b0;
    else if(state == IDLE )
        if(I_cfg_box_dir[1] == 1'b0)
            pixel_addr <= 0;
        else 
            pixel_addr <= I_cfg_win_col_num-1;
    else if (pixel_req)
        if(I_cfg_box_dir[1] == 1'b0)
            pixel_addr <= pixel_addr + 1'b1;
        else
            pixel_addr <= pixel_addr - 1'b1;
end
assign O_decode_col_addr = pixel_addr;
// write_over
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        write_over <= 1'b0;
    else if (pixel_req && pixel_count == 1'b1)
        write_over <= 1'b1;
    else
        write_over <= 1'b0;
end

// total_count
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        total_count <= 1'b0;
    else if (state == DEC0)
        total_count <= I_cfg_win_col_num/*I_cfg_sector_width * I_cfg_sector_count*/;
    else if (state == WREQ)
        total_count <= total_count - pixel_count - 1;
end

// pixel_count
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        pixel_count <= 1'b0;
    else if (state == WPREP) begin
        if (total_count >= pixel_max)
            pixel_count <= pixel_max - 1;
        else
            pixel_count <= total_count[8:0]-1;
    end
    else if (pixel_req)
        pixel_count <= pixel_count - 1'b1;
end

always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        pixel_max <= 1'b0;
    else
        pixel_max <= 8'd128;
end

// pixel_req
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        pixel_req <= 1'b0;
    else if (state == WPREP)
        pixel_req <= 1'b1;
    else if (write_over)
        pixel_req <= 1'b0;
end

// sector_id
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        sector_id <= 1'b0;
    else if (state == IDLE)
        sector_id <= 1'b0;
    else if (state == LOOP0 &&  next == LOOP1)
        sector_id <= sector_id + 1'b1;
end

// row_num
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        row_num <= 1'b0;
    else if (state == IDLE && I_row_start)
        row_num <= I_row_num;
end
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++


//{{{+++++++++++++++++++++addr decoder+++++++++++++++++++
assign O_decode_req    = (state == DEC0);
assign O_decode_row    = row_num;
assign O_decode_sector = sector_id[3:0];
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

//{{{+++++++++++++++++++++line buffer++++++++++++++++++++
assign O_row_ack   = (state == ACK);
reg pixel_req_d1 ;
reg pixel_req_d2 ;
assign O_pixel_req = pixel_req_d1;
always @(posedge I_sclk )begin
    pixel_req_d1 <= pixel_req;
    pixel_req_d2 <= pixel_req_d1;
end
assign O_write_valid = pixel_req_d2;
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

//{{{+++++++++++++++++++++write pixel++++++++++++++++++++
assign O_write_start = (state == WREQ);
assign O_write_addr  = write_addr;
assign O_write_len   = write_len[7:2];
assign O_write_data  = I_pixel_data;

// write_addr
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)begin
        write_addr      <= 1'b0;
    end
    else if (state == DEC1 && I_decode_done)
        write_addr <= I_decode_addr;
    else if(state == LOOP0)begin
        write_addr <= write_addr + 18'h2000 ;
    end
end


// write_len
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        write_len <= 1'b0;
    else if (state == WPREP) begin
        if (total_count >= pixel_max)
            write_len <= pixel_max;
        else
            write_len <= total_count[8:0] + 2'd3; // 按4的倍数向上取整
    end
end

//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++


endmodule

`default_nettype wire

// vim:set ts=4 sw=4 et fenc=utf-8 fdm=marker:
